# -*- coding: utf-8 -*-
import datetime
import json
from base64 import encodebytes
from urllib.parse import quote_plus

from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from django.conf import settings


class AliPay(object):

    def __init__(self, app_id, app_notify_url, app_return_url, app_pri_key_path, app_pub_key_path):
        self.app_id = app_id
        self.app_notify_url = app_notify_url
        self.app_return_url = app_return_url
        self.app_pri_key_path = app_pri_key_path
        self.app_pub_key_path = app_pub_key_path

        self.app_private_key = None
        self.app_public_key = None

        with open(self.app_pri_key_path) as f:
            self.app_private_key = RSA.importKey(f.read())

        with open(self.app_pub_key_path) as f:
            self.app_public_key = RSA.importKey(f.read())

    def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
        biz_content = {
            'out_trade_no': out_trade_no,
            'product_code': 'FAST_INSTANT_TRADE_PAY',
            'total_amount': total_amount,
            'subject': subject,
        }

        biz_content.update(kwargs)
        data = self.build_body(settings.PAY_METHOD, biz_content, self.app_return_url)
        return self.sign_data(data)

    def build_body(self, method, biz_content, return_url):
        data = {
            'app_id': self.app_id,
            'method': method,
            'charset': 'utf-8',
            'sign_type': 'RSA2',
            'timestamp': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'version': '1.0',
            'biz_content': biz_content
        }

        if return_url is not None:
            data['notify_url'] = self.app_notify_url
            data['return_url'] = self.app_return_url
        return data

    def sign_data(self, data):
        data.pop('sign', None)

        unsign_items = self.ordered_data(data)
        unsign_string = '&'.join(['{0}={1}'.format(k, v) for k, v in unsign_items])
        sign = self.sign(unsign_string.encode('utf-8'))
        quoted_string = '&'.join(['{0}={1}'.format(k, quote_plus(v)) for k, v in unsign_items])

        signed_string = quoted_string + '&sign=' + quote_plus(sign)
        return signed_string

    def ordered_data(self, data):
        complex_keys = []
        for k, v in data.items():
            if isinstance(v, dict):
                complex_keys.append(k)

        for k in complex_keys:
            data[k] = json.dumps(data[k], separators=(',', ':'))

        return sorted([(k, v) for k, v in data.items()])

    def sign(self, unsigned_string):
        key = self.app_private_key
        signer = PKCS1_v1_5.new(key)
        signature = signer.sign(SHA256.new(unsigned_string))
        sign_string = encodebytes(signature).decode('utf-8').replace('\n', '')
        return sign_string
